package com.example.service;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.example.common.Constants;
import com.example.common.enums.EmailCodeEnum;
import com.example.common.enums.ResultCodeEnum;
import com.example.common.enums.RoleEnum;
import com.example.controller.domain.UserRequest;
import com.example.entity.Account;
import com.example.entity.User;
import com.example.exception.CustomException;
import com.example.mapper.UserMapper;
import com.example.utils.EmailUtils;
import com.example.utils.RedisUtils;
import com.example.utils.TokenUtils;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 用户业务处理
 **/
@Service
@Slf4j
public class UserService {
    private static final long TIME_IN_MS5 = 5 * 60 * 1000;  // 表示5分钟的毫秒数
    @Resource
    private UserMapper userMapper;
    @Resource
    EmailUtils emailUtils;

    /**
     * 新增用户
     */
    public void add(User user) {
        User dbUser = userMapper.selectByUsername(user.getUsername());
        if (ObjectUtil.isNotNull(dbUser)) {
            throw new CustomException(ResultCodeEnum.USER_EXIST_ERROR);
        }
        if (ObjectUtil.isEmpty(user.getPassword())) {
            user.setPassword(Constants.USER_DEFAULT_PASSWORD);
        }
        if (ObjectUtil.isEmpty(user.getName())) {
            user.setName("系统用户" + RandomUtil.randomString(6));
        }
        user.setUid(IdUtil.fastSimpleUUID());
        user.setCreateTime(Timestamp.valueOf(LocalDateTime.now()));
        user.setRole(RoleEnum.USER.name());
        user.setDelTag("0");
        try {
            userMapper.insert(user);
        } catch (Exception e) {
            throw new RuntimeException("注册失败", e);
        }
    }

    /**
     * 删除
     */
    public void deleteById(Integer id) {
        userMapper.deleteById(id);
    }

    /**
     * 批量删除
     */
    public void deleteBatch(List<Integer> ids) {
        for (Integer id : ids) {
            userMapper.deleteById(id);
        }
    }

    /**
     * 修改
     */
    public void updateById(User user) {
        user.setUpdateTime(Timestamp.valueOf(LocalDateTime.now()));
        userMapper.updateById(user);
    }

    /**
     * 根据ID查询
     */
    public User selectById(Integer id) {
        return userMapper.selectById(id);
    }

    /**
     * 查询所有
     */
    public List<User> selectAll(User user) {
        return userMapper.selectAll(user);
    }

    /**
     * 分页查询
     */
    public PageInfo<User> selectPage(User user, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<User> list = userMapper.selectAll(user);
        return PageInfo.of(list);
    }

    /**
     * 登录
     */
    public Account login(UserRequest userRequest) {
        Account dbUser = userMapper.selectByUsername(userRequest.getUsername());
        if (ObjectUtil.isNull(dbUser)) {
            throw new CustomException(ResultCodeEnum.USER_NOT_EXIST_ERROR);//抛出用户不存在异常
        }
        if (!userRequest.getPassword().equals(dbUser.getPassword())) {
            throw new CustomException(ResultCodeEnum.USER_ACCOUNT_ERROR);//抛出用户密码错误异常
        }
        // 生成token
        String tokenData = dbUser.getId() + "-" + RoleEnum.USER.name() + "-" + dbUser.getName();
        String token = TokenUtils.createToken(tokenData, dbUser.getPassword());
        dbUser.setToken(token);
        return dbUser;
    }

    /**
     * 注册
     */
    public void register(UserRequest userRequest) {
        String key = Constants.EMAIL_CODE + EmailCodeEnum.REGISTER.getValue() + userRequest.getEmail();
        validateEmail(key, userRequest.getEmailCode());
        User saveUser = new User();
        BeanUtils.copyProperties(userRequest, saveUser);   // 把请求数据的属性copy给存储数据库的属性
        // 存储用户信息
        add(saveUser);
    }

    /**
     * 修改密码
     */
    public void updatePassword(UserRequest userRequest) {
        Integer userId = TokenUtils.getCurrentUser().getId();
        User dbUser = userMapper.selectById(userId);
        if (ObjectUtil.isNull(dbUser)) {
            throw new CustomException(ResultCodeEnum.USER_NOT_EXIST_ERROR);
        }
        if (!userRequest.getPassword().equals(dbUser.getPassword())) {
            throw new CustomException(ResultCodeEnum.PARAM_PASSWORD_ERROR);
        }
        dbUser.setPassword(userRequest.getNewPassword());
        try {
            userMapper.updateById(dbUser);
        } catch (Exception e) {
            throw new RuntimeException("修改密码失败", e);
        }
    }

    public User selectByUid(String uid) {
        return userMapper.selectByUid(uid);
    }

    public void sendEmail(String email, String type) {
        String emailPrefix = EmailCodeEnum.getValue(type);
        if (StrUtil.isBlank(emailPrefix)) {
            throw new CustomException("不支持的邮箱验证类型");
        }
        // 设置redis key
        String key = Constants.EMAIL_CODE + emailPrefix + email;
        Long expireTime = RedisUtils.getExpireTime(key);
        // 限制超过1分钟才可以继续发送邮件，判断过期时间是否大于4分钟
        if (expireTime != null && expireTime > 4 * 60) {
            throw new CustomException("发送邮箱验证过于频繁");
        }

        Integer code = Integer.valueOf(RandomUtil.randomNumbers(6));
        log.info("本次验证的code是：{}", code);
        String context = "<b>尊敬的用户：</b><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;您好，" +
                "文艺共鸣提醒您本次的验证码是：<b>{}</b>，" +
                "有效期5分钟。<br><br><br><b>文艺共鸣</b>";
        String html = StrUtil.format(context, code);
        // 校验邮箱是否已注册
        User user = userMapper.selectByEmail(email);
        if (EmailCodeEnum.REGISTER.equals(EmailCodeEnum.getEnum(type))) {  // 无需权限验证即可发送邮箱验证码
            if (user != null) {
                throw new CustomException("邮箱已注册");
            }
        } else if (EmailCodeEnum.RESET_PASSWORD.equals(EmailCodeEnum.getEnum(type))) {
            if (user == null) {
                throw new CustomException("未找到用户");
            }
        }
        // 忘记密码
        ThreadUtil.execAsync(() -> {   // 多线程执行异步请求，可以防止网络阻塞
            emailUtils.sendHtml("【文艺共鸣】验证提醒", html, email);

            RedisUtils.setCacheObject(key, code, TIME_IN_MS5, TimeUnit.MILLISECONDS);
        });
    }

    private void validateEmail(String emailKey, String emailCode) {
        // 校验邮箱
        Integer code = RedisUtils.getCacheObject(emailKey);
        if (code == null) {
            throw new CustomException("验证码失效");
        }
        if (!emailCode.equals(code.toString())) {
            throw new CustomException("验证码错误");
        }
        RedisUtils.deleteObject(emailKey);  // 清除缓存
    }


    public void resetPassword(UserRequest userRequest) {
        String email = userRequest.getEmail();
        User dbUser = userMapper.selectByEmail(userRequest.getEmail());
        if (ObjectUtil.isNull(dbUser)) {
            throw new CustomException(ResultCodeEnum.USER_NOT_EXIST_ERROR);
        }
        String key = Constants.EMAIL_CODE + EmailCodeEnum.RESET_PASSWORD.getValue() + email;
        validateEmail(key, userRequest.getEmailCode());
        dbUser.setPassword(Constants.USER_DEFAULT_PASSWORD);
        try {
            userMapper.updateById(dbUser);
        } catch (Exception e) {
            throw new RuntimeException("重置密码失败", e);
        }
    }
    public Account loginByEmailCode(UserRequest userRequest) {
        String email = userRequest.getEmail();
        User dbUser = userMapper.selectByEmail(userRequest.getEmail());
        if (ObjectUtil.isNull(dbUser)) {
            throw new CustomException(ResultCodeEnum.USER_NOT_EXIST_ERROR);
        }
        String key = Constants.EMAIL_CODE + EmailCodeEnum.LOGIN.getValue() + email;
        validateEmail(key, userRequest.getEmailCode());
        // 生成token
        String tokenData = dbUser.getId() + "-" + RoleEnum.USER.name() + "-" + dbUser.getName();
        String token = TokenUtils.createToken(tokenData, dbUser.getPassword());
        dbUser.setToken(token);
        return dbUser;
    }
}